// Generic DOM element creation
var createElement = function (p, type, className) {
	'use strict';
	var e = document.createElement(type);
	if(p) p.appendChild(e);
	if (className != undefined) e.className = className;
	return e;
};

// Base64
!function() {
	'use strict';

	function toInt32(bytes) {
		return (bytes[0] << 24) | (bytes[1] << 16) | (bytes[2] << 8) | bytes[3];
	}

	function getDimensions(data) {
		return {
			width: toInt32(data.slice(16, 20)),
			height: toInt32(data.slice(20, 24))
		};
	}

	var base64Characters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/';

	function base64Decode(data) {
		var result = [];
		var current = 0;

		for(var i = 0, c; c = data.charAt(i); i++) {
			if(c === '=') {
				if(i !== data.length - 1 && (i !== data.length - 2 || data.charAt(i + 1) !== '=')) {
					throw new SyntaxError('Unexpected padding character.');
				}

				break;
			}

			var index = base64Characters.indexOf(c);

			if(index === -1) {
				throw new SyntaxError('Invalid Base64 character.');
			}

			current = (current << 6) | index;

			if(i % 4 === 3) {
				result.push(current >> 16, (current & 0xff00) >> 8, current & 0xff);
				current = 0;
			}
		}

		if(i % 4 === 1) {
			throw new SyntaxError('Invalid length for a Base64 string.');
		}

		if(i % 4 === 2) {
			result.push(current >> 4);
		} else if(i % 4 === 3) {
			current <<= 6;
			result.push(current >> 16, (current & 0xff00) >> 8);
		}

		return result;
	}

	window.getPngDimensions = function(dataUri) {
		return getDimensions(base64Decode(dataUri.substring(22)));
	};
}();

/*********************/
/*** CLASS LIBRARY ***/
/*********************/

/* Simple JavaScript Inheritance
 * By John Resig http://ejohn.org/
 * MIT Licensed.
 */
// Inspired by base2 and Prototype
(function(){
  var initializing = !1, fnTest = /xyz/.test(function(){xyz;}) ? /\b_super\b/ : /.*/;
  // The base Class implementation (does nothing)
  this.Class = function(){};
  
  // Create a new Class that inherits from this class
  Class.extend = function(prop) {
    var _super = this.prototype;
    
    // Instantiate a base class (but only create the instance,
    // don't run the init constructor)
    initializing = !0;
    var prototype = new this();
    initializing = !1;
    
    // Copy the properties over onto the new prototype
    for (var name in prop) {
      // Check if we're overwriting an existing function
      prototype[name] = typeof prop[name] == "function" && 
        typeof _super[name] == "function" && fnTest.test(prop[name]) ?
        (function(name, fn){
          return function() {
            var tmp = this._super;
            
            // Add a new ._super() method that is the same method
            // but on the super-class
            this._super = _super[name];
            
            // The method only need to be bound temporarily, so we
            // remove it when we're done executing
            var ret = fn.apply(this, arguments);        
            this._super = tmp;
            
            return ret;
          };
        })(name, prop[name]) :
        prop[name];
    }
    
    // The dummy class constructor
    function Class() {
      // All construction is actually done in the init method
      if ( !initializing && this.init )
        this.init.apply(this, arguments);
    }
    
    // Populate our constructed prototype object
    Class.prototype = prototype;
    
    // Enforce the constructor to be what we expect
    Class.prototype.constructor = Class;

    // And make this class extendable
    Class.extend = arguments.callee;
    
    return Class;
  };
})();

/*********************/
/*** WATCH LIBRARY ***/
/*********************/

/**
 * jQuery Watch Plugin
 *
 * @author Darcy Clarke
 * @version 2.0
 *
 * Copyright (c) 2012 Darcy Clarke
 * Dual licensed under the MIT and GPL licenses.
 *
 * ADDS: 
 *
 * - $.watch()
 *  
 * USES:
 *
 * - DOMAttrModified event
 * 
 * FALLBACKS:
 * 
 * - propertychange event
 * - setTimeout() with delay 
 *
 * EXAMPLE:
 * 
 * $('div').watch('width height', function(){
 *      console.log(this.style.width, this.style.height);
 * });
 *
 * $('div').animate({width:'100px',height:'200px'}, 500);
 *
 */

(function($){
	'use strict';
    
    $.extend($.fn, {         
        
        /**
         * Watch Method
         * 
         * @param {String} the name of the properties to watch
         * @param {Object} options to overide defaults (only 'throttle' right now)
         * @param {Function} callback function to be executed when attributes change
         *
         * @return {jQuery Object} returns the jQuery object for chainability
         */   
        watch : function(props, options, callback){

            // Dummmy element
            var element = document.createElement('div');

            /**
             * Checks Support for Event
             * 
             * @param {String} the name of the event
             * @param {Element Object} the element to test support against
             *
             * @return {Boolean} returns result of test (true/false)
             */
            var isEventSupported = function(eventName, el) {
                eventName = 'on' + eventName;
                var supported = (eventName in el);
                if(!supported){
                    el.setAttribute(eventName, 'return;');
                    supported = typeof el[eventName] == 'function';
                }
                return supported;
            };

            // Type check options
            if(typeof(options) == 'function'){
                callback = options;
                options = {};
            }

            // Type check callback
            if(typeof(callback) != 'function')
                callback = function(){};

            // Map options over defaults
            options = $.extend({}, { throttle : 10 }, options);

            /**
             * Checks if properties have changed
             * 
             * @param {Element Object} the element to watch
             *
             */
            var check = function(el) {
                var data = el.data(),
                    changed = !1,
                    temp;

                // Loop through properties
                for(var i=0;i < data.props.length; i++){
                    temp = el.css(data.props[i]);
                    if(data.vals[i] != temp){
                        data.vals[i] = temp;
                        changed = !0;
                        break;
                    }
                }
                
                // Run callback if property has changed
                if(changed && data.cb)
                    data.cb.call(el, data);
            };

            return this.each(function(){
                var el = $(this),
                    cb = function(){ check.call(this, el) },
                    data = { props:props.split(','), cb:callback, vals: [] };
                $.each(data.props, function(i){ data.vals[i] = el.css(data.props[i]); });
                el.data(data);
                if(isEventSupported('DOMAttrModified', element)){
                    el.on('DOMAttrModified', callback);
                } else if(isEventSupported('propertychange', element)){
                    el.on('propertychange', callback);
                } else {
                    setInterval(cb, options.throttle);
                }
            });
        }
    });
})(jQuery);